// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "AbilitySystemInterface.h"
#include "GameplayCueInterface.h"
#include "GameplayTagAssetInterface.h"
#include "ModularCharacter.h"
#include "Teams/LyraTeamAgentInterface.h"

#include "LyraCharacter.generated.h"

#define UE_API LYRAGAME_API

class AActor;
class AController;
class ALyraPlayerController;
class ALyraPlayerState;
class FLifetimeProperty;
class IRepChangedPropertyTracker;
class UAbilitySystemComponent;
class UInputComponent;
class ULyraAbilitySystemComponent;
class ULyraCameraComponent;
class ULyraHealthComponent;
class ULyraPawnExtensionComponent;
class UObject;
struct FFrame;
struct FGameplayTag;
struct FGameplayTagContainer;

/**
 * FLyraReplicatedAcceleration: 加速度的压缩表示
 */
USTRUCT()
struct FLyraReplicatedAcceleration
{
	GENERATED_BODY()

	UPROPERTY()
	uint8 AccelXYRadians = 0;	// XY加速度分量的方向，量化表示[0, 2*pi]

	UPROPERTY()
	uint8 AccelXYMagnitude = 0;	// XY分量的加速度率，量化表示[0, MaxAcceleration]

	UPROPERTY()
	int8 AccelZ = 0;	// 原始Z加速度率分量，量化表示[-MaxAcceleration, MaxAcceleration]
};

/** 我们用于发送快速共享移动更新的类型。 */
USTRUCT()
struct FSharedRepMovement
{
	GENERATED_BODY()

	FSharedRepMovement();

	bool FillForCharacter(ACharacter* Character);
	bool Equals(const FSharedRepMovement& Other, ACharacter* Character) const;

	bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess);

	UPROPERTY(Transient)
	FRepMovement RepMovement;

	UPROPERTY(Transient)
	float RepTimeStamp = 0.0f; // 复制时间戳

	UPROPERTY(Transient)
	uint8 RepMovementMode = 0; // 复制移动模式

	UPROPERTY(Transient)
	bool bProxyIsJumpForceApplied = false; // 代理是否应用了跳跃力

	UPROPERTY(Transient)
	bool bIsCrouched = false; // 是否处于下蹲状态
};

template<>
struct TStructOpsTypeTraits<FSharedRepMovement> : public TStructOpsTypeTraitsBase2<FSharedRepMovement>
{
	enum
	{
		WithNetSerializer = true, // 具有网络序列化器
		WithNetSharedSerialization = true, // 具有网络共享序列化
	};
};

/**
 * ALyraCharacter
 *
 *	本项目使用的基础角色Pawn类。
 *	负责向Pawn组件发送事件。
 *	新行为应尽可能通过Pawn组件添加。
 */
UCLASS(MinimalAPI, Config = Game, Meta = (ShortTooltip = "The base character pawn class used by this project."))
class ALyraCharacter : public AModularCharacter, public IAbilitySystemInterface, public IGameplayCueInterface, public IGameplayTagAssetInterface, public ILyraTeamAgentInterface
{
	GENERATED_BODY()

public:

	UE_API ALyraCharacter(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());

	/**
	 * 获取Lyra玩家控制器
	 */
	UFUNCTION(BlueprintCallable, Category = "Lyra|Character")
	UE_API ALyraPlayerController* GetLyraPlayerController() const;

	/**
	 * 获取Lyra玩家状态
	 */
	UFUNCTION(BlueprintCallable, Category = "Lyra|Character")
	UE_API ALyraPlayerState* GetLyraPlayerState() const;

	/**
	 * 获取Lyra能力系统组件
	 */
	UFUNCTION(BlueprintCallable, Category = "Lyra|Character")
	UE_API ULyraAbilitySystemComponent* GetLyraAbilitySystemComponent() const;
	/**
	 * 获取能力系统组件（重写自IAbilitySystemInterface）
	 */
	UE_API virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;

	/**
	 * 获取拥有的游戏标签（重写自IGameplayTagAssetInterface）
	 */
	UE_API virtual void GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const override;
	/**
	 * 检查是否匹配指定的游戏标签（重写自IGameplayTagAssetInterface）
	 */
	UE_API virtual bool HasMatchingGameplayTag(FGameplayTag TagToCheck) const override;
	/**
	 * 检查是否匹配所有指定的游戏标签（重写自IGameplayTagAssetInterface）
	 */
	UE_API virtual bool HasAllMatchingGameplayTags(const FGameplayTagContainer& TagContainer) const override;
	/**
	 * 检查是否匹配任何指定的游戏标签（重写自IGameplayTagAssetInterface）
	 */
	UE_API virtual bool HasAnyMatchingGameplayTags(const FGameplayTagContainer& TagContainer) const override;

	/**
	 * 切换下蹲状态
	 */
	UE_API void ToggleCrouch();

	//~AActor 接口
	/**
	 * 预初始化组件（重写自AActor）
	 */
	UE_API virtual void PreInitializeComponents() override;
	/**
	 * 开始游戏（重写自AActor）
	 */
	UE_API virtual void BeginPlay() override;
	/**
	 * 结束游戏（重写自AActor）
	 */
	UE_API virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
	/**
	 * 重置（重写自AActor）
	 */
	UE_API virtual void Reset() override;
	/**
	 * 获取生命周期复制属性（重写自AActor）
	 */
	UE_API virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
	/**
	 * 预复制（重写自AActor）
	 */
	UE_API virtual void PreReplication(IRepChangedPropertyTracker& ChangedPropertyTracker) override;
	//~AActor 接口结束

	//~APawn 接口
	/**
	 * 通知控制器改变（重写自APawn）
	 */
	UE_API virtual void NotifyControllerChanged() override;
	//~APawn 接口结束

	//~ILyraTeamAgentInterface 接口
	/**
	 * 设置通用团队ID（重写自ILyraTeamAgentInterface）
	 */
	UE_API virtual void SetGenericTeamId(const FGenericTeamId& NewTeamID) override;
	/**
	 * 获取通用团队ID（重写自ILyraTeamAgentInterface）
	 */
	UE_API virtual FGenericTeamId GetGenericTeamId() const override;
	/**
	 * 获取团队索引改变委托（重写自ILyraTeamAgentInterface）
	 */
	UE_API virtual FOnLyraTeamIndexChangedDelegate* GetOnTeamIndexChangedDelegate() override;
	//~ILyraTeamAgentInterface 接口结束

	/**
	 * 快速共享复制：在跳过默认属性复制的帧上调用。向所有人复制单个移动更新。
	 */
	UFUNCTION(NetMulticast, unreliable)
	UE_API void FastSharedReplication(const FSharedRepMovement& SharedRepMovement);

	// 上次发送的FSharedRepMovement，避免重复发送。
	FSharedRepMovement LastSharedReplication;

	/**
	 * 更新共享复制
	 */
	UE_API virtual bool UpdateSharedReplication();

protected:

	/**
	 * 能力系统初始化时调用
	 */
	UE_API virtual void OnAbilitySystemInitialized();
	/**
	 * 能力系统取消初始化时调用
	 */
	UE_API virtual void OnAbilitySystemUninitialized();

	/**
	 * 被控制器拥有时调用（重写自APawn）
	 */
	UE_API virtual void PossessedBy(AController* NewController) override;
	/**
	 * 取消拥有时调用（重写自APawn）
	 */
	UE_API virtual void UnPossessed() override;

	/**
	 * 控制器复制时调用（重写自APawn）
	 */
	UE_API virtual void OnRep_Controller() override;
	/**
	 * 玩家状态复制时调用（重写自APawn）
	 */
	UE_API virtual void OnRep_PlayerState() override;

	/**
	 * 设置玩家输入组件（重写自APawn）
	 */
	UE_API virtual void SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) override;

	/**
	 * 初始化游戏标签
	 */
	UE_API void InitializeGameplayTags();

	/**
	 * 掉出世界时调用（重写自APawn）
	 */
	UE_API virtual void FellOutOfWorld(const class UDamageType& dmgType) override;

	/**
	 * 开始角色死亡序列（禁用碰撞，禁用移动等...）
	 */
	UFUNCTION()
	UE_API virtual void OnDeathStarted(AActor* OwningActor);

	/**
	 * 结束角色死亡序列（分离控制器，销毁Pawn等...）
	 */
	UFUNCTION()
	UE_API virtual void OnDeathFinished(AActor* OwningActor);

	/**
	 * 禁用移动和碰撞
	 */
	UE_API void DisableMovementAndCollision();
	/**
	 * 由于死亡而销毁
	 */
	UE_API void DestroyDueToDeath();
	/**
	 * 取消初始化并销毁
	 */
	UE_API void UninitAndDestroy();

	/**
	 * 当角色的死亡序列完成时调用（蓝图可实现事件）
	 */
	UFUNCTION(BlueprintImplementableEvent, meta=(DisplayName="OnDeathFinished"))
	UE_API void K2_OnDeathFinished();

	/**
	 * 移动模式改变时调用（重写自ACharacter）
	 */
	UE_API virtual void OnMovementModeChanged(EMovementMode PrevMovementMode, uint8 PreviousCustomMode) override;
	/**
	 * 设置移动模式标签
	 */
	UE_API void SetMovementModeTag(EMovementMode MovementMode, uint8 CustomMovementMode, bool bTagEnabled);

	/**
	 * 开始下蹲时调用（重写自ACharacter）
	 */
	UE_API virtual void OnStartCrouch(float HalfHeightAdjust, float ScaledHalfHeightAdjust) override;
	/**
	 * 结束下蹲时调用（重写自ACharacter）
	 */
	UE_API virtual void OnEndCrouch(float HalfHeightAdjust, float ScaledHalfHeightAdjust) override;

	/**
	 * 检查是否可以跳跃内部实现（重写自ACharacter）
	 */
	UE_API virtual bool CanJumpInternal_Implementation() const;

private:

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Lyra|Character", Meta = (AllowPrivateAccess = "true"))
	TObjectPtr<ULyraPawnExtensionComponent> PawnExtComponent; // Pawn扩展组件

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Lyra|Character", Meta = (AllowPrivateAccess = "true"))
	TObjectPtr<ULyraHealthComponent> HealthComponent; // 健康组件

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Lyra|Character", Meta = (AllowPrivateAccess = "true"))
	TObjectPtr<ULyraCameraComponent> CameraComponent; // 相机组件

	UPROPERTY(Transient, ReplicatedUsing = OnRep_ReplicatedAcceleration)
	FLyraReplicatedAcceleration ReplicatedAcceleration; // 复制的加速度

	UPROPERTY(ReplicatedUsing = OnRep_MyTeamID)
	FGenericTeamId MyTeamID; // 我的团队ID

	UPROPERTY()
	FOnLyraTeamIndexChangedDelegate OnTeamChangedDelegate; // 团队改变委托

protected:
	/**
	 * 在拥有结束时确定新的团队ID
	 */
	virtual FGenericTeamId DetermineNewTeamAfterPossessionEnds(FGenericTeamId OldTeamID) const
	{
		// 这可以更改为返回，例如，OldTeamID，如果你想在之后保持分配，或者返回某个中立派系的ID，等等...
		return FGenericTeamId::NoTeam;
	}

private:
	/**
	 * 控制器改变团队时调用
	 */
	UFUNCTION()
	UE_API void OnControllerChangedTeam(UObject* TeamAgent, int32 OldTeam, int32 NewTeam);

	/**
	 * 复制加速度改变时调用
	 */
	UFUNCTION()
	UE_API void OnRep_ReplicatedAcceleration();

	/**
	 * 我的团队ID复制时调用
	 */
	UFUNCTION()
	UE_API void OnRep_MyTeamID(FGenericTeamId OldTeamID);
};

#undef UE_API